package ar.edu.itba.it.pdc.protocol.email;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.joda.time.DateTime;

import ar.edu.itba.it.pdc.app.Madonna3;
import ar.edu.itba.it.pdc.protocol.POP3Utils;
import ar.edu.itba.it.pdc.protocol.exception.IllegalMailRequest;
import ar.edu.itba.it.pdc.proxy.filters.FilterRunner;
import ar.edu.itba.it.pdc.utils.TempFilesSetup;
import ar.edu.itba.it.pdc.utils.TimeConverter;

import com.google.common.io.LineReader;

public class Mail {

	private final static File DIR = TempFilesSetup.DIR;
	private final static String CRLF = TempFilesSetup.CRLF;
	private final static String SUFFIX = TempFilesSetup.SUFFIX;
	private final static String PREFIX = TempFilesSetup.PREFIX_MAIL;
	
	private Header from;
	private DateTime date;
	private Header contentType;
	private Header contentTransferEncoding;
	protected File body;
	
	private List<Part> parts;	
	private List<Header> headers = new LinkedList<Header>();

	public static Mail build(InputStream input){
		BufferedReader reader = new BufferedReader(new InputStreamReader(input));
		Mail mail = new Mail(reader);
		return mail;
	}
	
	private Mail(BufferedReader reader) {
		parseMail(reader);
		if(getContentType() != null && getContentType().contains("multipart")){
			if (this.parts == null)
				this.parts = new ArrayList<Part>();
			partsGenerator();
		}
	}

	private void parseMail(BufferedReader reader) {
		String line = null;
		String sheader = "";
		try {
			//Skips first line which is response status
			reader.readLine();
			
			//Get mail headers
			while (isValidLine(line = reader.readLine(), getBoundary()) && !line.isEmpty()){
				if (line.startsWith(" ") || line.startsWith("\t")) {
					sheader += line;
				} else {
					if (!sheader.isEmpty()) {
						if (sheader.contains(":")){
							String unfolded = POP3Utils.unfold(sheader);
							String parts[] = unfolded.split(":", 2);
							addHeader(new Header(parts[0], parts[1]));
						} else
							sheader += line;
						}
					sheader = "";
					sheader += line;
				}
			}
			
			if (line == null)
				return;
			
			//Get full mail body
			this.body = File.createTempFile(PREFIX, SUFFIX, DIR);
			FileWriter writer = new FileWriter(this.body);
			
			writer.write(line + CRLF);
			
			while((line = reader.readLine()) != null){
				writer.write(line + CRLF);
				writer.flush();
			}
			
			writer.flush();
		} catch (IOException e) {
			Madonna3.log.error("Problems parsing mail: " + e.getMessage());
		}
	}
	
	private void partsGenerator() {
		try {
			LineReader reader = new LineReader(new InputStreamReader(new FileInputStream(this.body)));
			String line = null;
			String boundary = getBoundary();
			StringBuffer buffer = new StringBuffer();
			
			while((line = reader.readLine()) != null && !line.equals("--" + boundary + "--")){
				if (line.equals("--" + boundary)){
					if (buffer.length() != 0){
						this.parts.add(new Part(new StringReader(buffer.toString()), boundary));
						buffer.delete(0, buffer.length());
					}
				} else
					buffer.append(line + CRLF);
			}
			this.parts.add(new Part(new StringReader(buffer.toString()), boundary));
			buffer.delete(0, buffer.length());
		} catch(IOException e) {
			Madonna3.log.error("Problems parsing multipart mail: " + e.getMessage());		
		}	
	}
	
	public void replaceWith(Mail mail) {
		this.from = mail.from;
		this.date = mail.date;
		this.contentType = mail.contentType;
		this.contentTransferEncoding = mail.contentTransferEncoding;
		this.body = mail.body;
		this.headers = mail.headers;
	}
	
	private boolean isValidLine(String line, String boundary){
		if(line != null && (boundary == null || !line.contains(boundary)))
			return true;
		return false;
	}

	public void addHeader(Header header) {
		headers.add(header);
		if (header.getName().equals("Content-Type")) {
			contentType = header;
		} else if (header.getName().equals("Content-Transfer-Encoding")) {
			contentTransferEncoding = header;
		} else if (header.getName().equals("Date")) {
			date = TimeConverter.parseDateHeaderTime(header);
		} else if (header.getName().equals("From")) {
			from = header;
		}
	}

	public Iterable<Header> getHeaders() {
		return headers;
	}

	public String getUnfoldedHeaders() {
		String resp = "";
		for(Header h : getHeaders()) {
			resp += POP3Utils.unfold(h.getHeader());
			resp += CRLF;
		}
		return resp + CRLF;
	}
	
	public String getContentType() {
		if (contentType == null)
			return null;
		return contentType.getValue().trim();
	}

	public Header getHeader(String name) {
		for (Header h : getHeaders()) {
			if (h.getName().toLowerCase().equals(name.toLowerCase())) {
				return h;
			}
		}
		return null;
	}

	public Header getFrom() {
		return from;
	}

	public DateTime getDate() {
		return date;
	}

	public Header getContentTransferEncoding() {
		return contentTransferEncoding;
	}

	public void writeTo(OutputStream out) {
		applyFilters();
		try {
			PrintWriter writer = new PrintWriter(new OutputStreamWriter(out));
			for (Header h : getHeaders()){
				writer.write(POP3Utils.fold(0, h.getHeader()) + CRLF);
			}
			String line;
			BufferedReader reader = getBody();
			while((line = reader.readLine()) != null) {
				writer.write(CRLF);
				writer.write(line);
				writer.flush();
			}
			writer.flush();
		} catch (IOException e) {
			Madonna3.log.error("Problems sending message: " + e.getMessage());
		}
	}
	
	public void applyFilters() {
		FilterRunner.run(this);
	}

	public BufferedReader getBody() {
		if(this.body == null) {
			/*try {
				this.body = File.createTempFile(PREFIX, SUFFIX, DIR);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}*/
			throw new IllegalMailRequest("This mail must be deleted");
		}
		try {
			return new BufferedReader(new FileReader(this.body));
		} catch (Exception e) {
			Madonna3.log.error("Problem with the tmpFile");
			return null;
		}
	}
	
	public long getSize(){
		return this.body.length();
	}
	
	public List<Part> getParts() {
		return parts;
	}

	private String getBoundary() {
		if (this.contentType == null || !this.getContentType().contains("boundary="))
			return null;
		String boundary = this.getContentType().split("boundary=", 2)[1];
		//Remuevo las comillas al principio y fin de la cadena.
		boundary = boundary.substring(1, boundary.length()-1);
		return boundary;
	}

	@Override
	protected void finalize() throws Throwable {
		this.body.delete();
		super.finalize();
	}

	
}
